package com.shimao.celnet.payment.common.apiAop;

import com.shimao.celnet.payment.form.IAppInterfaceForm;
import com.shimao.celnet.payment.model.IInterfaceLog;
import com.shimao.celnet.payment.model.response.ErrorInfoEnum;
import com.shimao.celnet.payment.model.response.ResponseJson;
import com.shimao.celnet.payment.service.IAppInterfaceService;
import com.shimao.celnet.payment.service.IInterfaceLogService;
import com.shimao.celnet.payment.service.PPayCustService;
import com.shimao.celnet.payment.util.FinalUtil;
import com.shimao.celnet.payment.util.SignCheckUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * 对通联的所有接口进行验证
 * Created by ensure.xu on 2017/12/9.
 * 实现AOP的切面主要有以下几个要素：
 * <p>
 * 使用@Aspect注解将一个java类定义为切面类
 * 使用@Pointcut定义一个切入点，可以是一个规则表达式，比如下例中某个package下的所有函数，也可以是一个注解等。
 * 根据需要在切入点不同位置的切入内容
 * 使用@Before在切入点开始处切入内容
 * 使用@After在切入点结尾处切入内容
 * 使用@AfterReturning在切入点return内容之后切入内容（可以用来对处理返回值做一些加工处理）
 * 使用@Around在切入点前后切入内容，并自己控制何时执行切入点自身的内容
 * 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
 */
@Aspect
@Component
public class ApiTLAop {
    protected Logger logger = Logger.getLogger(this.getClass());

    @Autowired//接口应用注册Service
    private IAppInterfaceService iAppInterfaceService;

    @Autowired//接口日志Service
    private IInterfaceLogService interfaceLogService;
    @Autowired
    private PPayCustService pPayCustService;

    //定义日志类，存储接口日志
    private IInterfaceLog iInterfaceLog;
    private HttpServletRequest request;
    private HttpServletResponse response;
    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private String requestText; //请求body
    private String responseText; //返回body
    TreeMap<String, String> params = new TreeMap<String, String>();
    private String errorCode;//错误码
    private String errorMsg;//错误信息

    /**
     * 定义拦截规则：拦截com.shimao.celnet.payment.controller.api包下面的所有类中，有@RequestMapping注解的方法。
     */
    @Pointcut("execution(* com.shimao.celnet.payment.controller.apitl..*(..)) @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    private void checkTL() {
    }


    /**
     * 拦截器具体实现 ,从request中取得通联信息，进行解签
     * 1、把参数按照ascll排序，进行拼接在拼接的前后加上token
     * 2、进行MD5加密
     * 3、验证签名合法性
     *
     * @param proceedingJoinPoint
     * @throws Throwable
     */
    @Around("checkTL()")
    public void doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        logger.info("接口：切点进入");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        this.request = attributes.getRequest();
        this.response = attributes.getResponse();
        this.setResponse(this.response);
        PrintWriter pw = this.response.getWriter();
        ResponseJson retJson = new ResponseJson(ErrorInfoEnum.SUCCESS, "");//初始化错误码和错误信息
        errorCode = ErrorInfoEnum.SUCCESS.getErrorCode();
        errorMsg = ErrorInfoEnum.SUCCESS.getErrorMessage();
        startTimeMillis = System.currentTimeMillis(); // 记录方法开始执行的时间
        Object[] intA = proceedingJoinPoint.getArgs();//获取请求json
        requestText = intA[0].toString();
        params = getParams(request);
        //从request中取得通联信息，获取token
        Map<String, String> pMap = new HashMap<String, String>();
        //应用ID-->通联的appid应用id
        pMap.put("appId", params.get("appid"));
        //接口ID-->通联的商户id
        pMap.put("custId", params.get("cusid"));
        String token = pPayCustService.getBySfGuid(pMap).getAppKey();
        System.out.println("=====================================" + token);
        //********* 1、********************
        //验证签名，防止数据篡改和攻击
        boolean isSign = false;
        isSign = SignCheckUtil.validSign(params, token);
        if (isSign) {
            Object result = proceedingJoinPoint.proceed();
            responseText = result.toString();
            pw.print(result.toString());
            pw.close();
        } else {
            //签名错误 1012
            errorCode = ErrorInfoEnum.ERROR_SIGN.getErrorCode();//错误码
            errorMsg = ErrorInfoEnum.ERROR_SIGN.getErrorMessage();//错误信息
        }
        logger.info("接口：切点完成");
        //输出
        responseText = retJson.toString();
        pw.print(retJson.toString());
        pw.close();//注意关闭输出流
    }

    /**
     * 记录接口日志,正常情况下
     *
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "checkTL()")
    public void doAfterReturning(Object ret) throws Throwable {
        this.saveLogger(ret == null ? null : ret.toString());
    }

    /**
     * 记录接口日志,抛出异常
     *
     * @param ex
     * @param joinPoint
     * @throws Throwable
     */
    @AfterThrowing(throwing = "ex", pointcut = "checkTL()")
    public void doRecoveryActions(JoinPoint joinPoint, Exception ex) throws IOException {
        String methodName = joinPoint.getSignature().getName();
        String exVar = "this method " + methodName + " end.ex message<" + ex + ">";
        logger.info(exVar);
        this.saveLogger(exVar);
    }

    /**
     * 公共日志存储，用于正常执行和异常执行的时候调用
     *
     * @param StrVar
     */
    public void saveLogger(String StrVar) throws IOException {
        endTimeMillis = System.currentTimeMillis(); // 记录方法执行完成的时间
        String loginUserGuid = FinalUtil.INTER_FACE_USER_ID;
        params = getParams(request);
        IAppInterfaceForm appInterfaceBean = iAppInterfaceService.getInfoByAppIfKey(params.get("appid"), params.get("cusid"));
        iInterfaceLog = new IInterfaceLog();
        iInterfaceLog.setGuid(interfaceLogService.getGuid());//记录 GUID
        iInterfaceLog.setCreatedUserGuid(loginUserGuid);//创建人 GUID
        iInterfaceLog.setUpdateUserGuid(loginUserGuid);//修改人 GUID
        iInterfaceLog.setInterfaceId(appInterfaceBean.getInterfaceId());//接口编号
        iInterfaceLog.setInterfaceApi(appInterfaceBean.getInterfaceApi());// 接口api
        iInterfaceLog.setInterfaceName(appInterfaceBean.getInterfaceName());//接口名称
        iInterfaceLog.setInterfaceType(appInterfaceBean.getInterfaceType());// 调用类型
        iInterfaceLog.setAppKey(appInterfaceBean.getAppKey());// 应用系统编号
        iInterfaceLog.setAppName(appInterfaceBean.getAppName());//应用系统名称
        iInterfaceLog.setAppToken(appInterfaceBean.getAppToken());// 应用token
        iInterfaceLog.setRequestUrl(appInterfaceBean.getRequestUrl());//访问url
        iInterfaceLog.setStartDate(String.valueOf(startTimeMillis));
        iInterfaceLog.setEndDate(String.valueOf(endTimeMillis));// 结束时间
        iInterfaceLog.setConsumeTime(String.valueOf(endTimeMillis - startTimeMillis));//耗时
        iInterfaceLog.setRequestText(requestText);// 请求文本
        iInterfaceLog.setErrorcode(errorCode);// 错误编码
        iInterfaceLog.setErrormsg(errorMsg);//错误信息
        iInterfaceLog.setCreatedDate(new Date()); // 创建日期
        iInterfaceLog.setUpdateDate(new Date());// 更新日期
        if (StringUtils.isNotEmpty(StrVar)) {
            iInterfaceLog.setExceptionInfo(StrVar);//异常信息
            iInterfaceLog.setStatus("2");// 接口状态1:失败，0:成功，2:异常
        } else {
            iInterfaceLog.setResponseText(responseText);//应答文本
            iInterfaceLog.setStatus("0");// 接口状态1:失败，0:成功，2:异常
        }
        interfaceLogService.save(iInterfaceLog);
        // 处理完请求，返回内容
        logger.info("日志记录-完成");

    }

    /**
     * 设置response的格式
     *
     * @param response
     */
    public void setResponse(HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=UTF-8");
        response.addHeader("Access-Control-Allow-Origin", "*");
        this.response = response;
    }

    /**
     * 动态遍历获取通联返回的参数
     *
     * @param request
     * @return
     * @throws IOException
     */
    private TreeMap<String, String> getParams(HttpServletRequest request) throws IOException {
        request.setCharacterEncoding("utf-8");
        TreeMap<String, String> map = new TreeMap<String, String>();
        Map reqMap = request.getParameterMap();
        for (Object key : reqMap.keySet()) {
            String value = ((String[]) reqMap.get(key))[0];
            System.out.println("-------打印输出--------" + key + ":" + value);
            map.put(key.toString(), value);
        }
        return map;
    }
}